import {
  CanActivate,
  ExecutionContext,
  Inject,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { JwtService } from '@nestjs/jwt';
import { Request } from 'express';
import { IS_PUBLIC_KEY } from './utils/public.metadata';
import { token_secret_key } from './utils/secret';
import { UserActionService } from 'src/user-action/user-action.service';
import { LOG_ACTION_KEY } from './utils/logging.metadata';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(
    private jwtService: JwtService,
    private reflector: Reflector,
  ) {}

  @Inject()
  userActionService: UserActionService;
  async canActivate(context: ExecutionContext): Promise<boolean> {
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    const loggingActionDetail = this.reflector.getAllAndOverride<string>(
      LOG_ACTION_KEY,
      [context.getHandler(), context.getClass()],
    );
    const request: Request = context.switchToHttp().getRequest();
    //  获取token
    const token = this.extractTokenFromHeader(request);
    if (!token) {
      if (isPublic) {
        // 💡 See this condition
        return true;
      }
      throw new UnauthorizedException();
    }
    try {
      const payload: { userId: number; userName: string } =
        await this.jwtService.verifyAsync(token, {
          secret: token_secret_key,
        });
      // 💡 We're assigning the payload to the request object here
      // so that we can access it in our route handlers
      request['user'] = payload;
      // 获取ip
      const ip = request.ip;
      const platform = request.headers['user-agent'];
      // 存储日志
      if (loggingActionDetail) {
        void this.userActionService.create({
          userId: payload.userId,
          ip: ip || 'unkown',
          info: loggingActionDetail,
          platform: platform || 'unkown',
        });
      }
    } catch {
      if (isPublic) {
        return true;
      }
      throw new UnauthorizedException();
    }
    return true;
  }

  private extractTokenFromHeader(request: Request): string | undefined {
    const [type, token] = request.headers.authorization?.split(' ') ?? [];
    return type === 'Bearer' ? token : undefined;
  }
}
